package com.ai.ly.goodsautocat.isphone;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

import com.ai.ly.goodsautocat.common.Constants;
import com.ai.ly.goodsautocat.common.ModelManager;
import com.ai.ly.goodsautocat.common.SampleFactory;
import com.ai.ly.goodsautocat.common.SampleInfoBuilderFactory;
import com.ai.ly.goodsautocat.util.FileUtil;
import com.ai.ly.goodsautocat.vo.Fscore;
import com.ai.ly.goodsautocat.vo.SampleVO;
import com.ai.ly.goodsautocat.vo.TrainningSampleVO;

import libsvm.svm;
import libsvm.svm_model;
import libsvm.svm_node;
import libsvm.svm_parameter;
import libsvm.svm_problem;

/**
 * 
 * Title: 使用SVM进行训练 <br>
 * Description: <br>
 * Date: 2018年4月4日 <br>
 * Copyright (c) 2018 AILK <br>
 * 
 * @author liangyi
 */
public class TrainIsPhoneWithSVM {
    
    /**最多训练多少个样本*/
    static final int MAX_SAMPLE_NUMBER=Integer.MAX_VALUE;
    
    /**正样本最大数量*/
    static final int MAX_POS_SAMPLE_NUMBER=3000;
    /**负样本最大数量*/
    static final int MAX_NEG_SAMPLE_NUMBER=MAX_POS_SAMPLE_NUMBER;
    
    static String modelFilePath="data\\sample(isphone)\\model.json";
    
    public svm_model model;
    public svm_problem prob;

    public static void main(String[] args) throws Exception {
        TrainIsPhoneWithSVM train=new TrainIsPhoneWithSVM();
        
        //开始训练
        train.train();
        
        //保存model
        train.saveModelFile();
        
        //TMP-------------------------------
        //对训练样本进行测试，看看准确率
        System.out.println("开始使用训练样本进行验证");
        double[] predictY=svm.svm_predict(train.model, train.prob.x);
        Fscore fscore=FscoreBuilder.buildFscore(train.prob.y, predictY);
        System.out.println("使用训练样本进行验证，得到结果如下："+fscore);
        //TMP-------------------------------
    }
    
    public void saveModelFile() throws Exception {
        File modelFile=FileUtil.getFileInSys(modelFilePath);
        ModelManager.saveModel(model, modelFile);
    }
    
    public svm_model train() {
        //加载样本
        this.prob = loadTrainSample();
        
        svm_parameter param = new svm_parameter();
        //param.svm_type=svm_parameter.ONE_CLASS;
        param.kernel_type=svm_parameter.LINEAR;
        param.C=0.5;
        
        //开始训练
        System.out.println("开始训练样本");
        long t1=System.currentTimeMillis();
        svm_model model = svm.svm_train(prob, param);
        long t2=System.currentTimeMillis();
        System.out.println("训练样本完毕，用时:"+(t2-t1)/1000+"秒");
        
        this.model=model;
        return model;
    }

    /**
     * 加载样本数据
     * @return
     * @author liangyi
     */
    public svm_problem loadTrainSample() {
        long t1=System.currentTimeMillis();
        System.out.println("开始加载样本");
        
        //读取样本文件获取所有样本信息-----------------------------------------------------------
        File orgSampleFile=FileUtil.getFileInSys(Constants.orgSampleFilePath);
        Map<String, TrainningSampleVO> sampleMap=new HashMap<>();
        try {
            BufferedReader e = new BufferedReader(new FileReader(orgSampleFile));
            for (String lineContent = e.readLine(); lineContent != null; lineContent = e.readLine()) {
                //解析每行数据
                int lastIndex=lineContent.lastIndexOf(",");
                if (lastIndex<=0) {
                    continue;
                }
                
                String gdsName=lineContent.substring(0, lastIndex);
                //商品分类
                String catId=lineContent.substring(lastIndex+1, lineContent.length());
                
                TrainningSampleVO sample=SampleFactory.buildTrainningSample(gdsName, catId,SampleInfoBuilderFactory.getBuilderForIsphone());
                if (sample==null) {
                    continue;
                }
                
                //要过滤掉相同的样本(样本相同的话，sample中indexCountMap、y的内容必定是相同的)
                sampleMap.put(sample.getIndexCountAndYStr(), sample);
                
                if (sampleMap.size()>=MAX_SAMPLE_NUMBER) {
                    break;
                }
            }
            e.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //读取样本文件获取所有样本信息end-----------------------------------------------------------
        System.out.println("从样本文件中获取到了"+sampleMap.size()+"个有效的样本");
        
        //将样本封装成svm所需的格式
        svm_problem prob=buildSvmProblem(sampleMap);
        
        long t2=System.currentTimeMillis();
        System.out.println("加载样本完毕，用时:"+(t2-t1)/1000+"秒");
        return prob;
    }
    
    public svm_problem buildSvmProblem(Map<String, TrainningSampleVO> sampleMap) {
        //打印多少个正样本，多少个负样本
        int posSampleCount=0;
        int negSampleCount=0;
        //将样本封装成svm所需的格式
        svm_problem prob=new svm_problem();
        int xLength=DictMapOfIsPhone.getDictSize();
        /**训练数据的数目*/
        int l=sampleMap.size();
        /**包含其目标值的数组（分类时是整数，回归时是实数）*/
        double[] y=new double[l];
        /**一个指针的数组，每一个指针指向一个训练向量的离散表示（svm_node的数组）*/
        svm_node[][] X=new svm_node[l][xLength];
        
        //生成训练数据
        int index=0;
        for (Map.Entry<String, TrainningSampleVO> entry : sampleMap.entrySet()) {
            TrainningSampleVO sample=entry.getValue();
            
            svm_node[] nodeArr=buildSvmNodes(sample);
            
            X[index]=nodeArr;
            y[index]=sample.getY();
            
            index++;
            
            if (index%1000==0) {
                System.out.println("已封装"+index+"个样本");
            }
            
            if (sample.getY()>0) {
                posSampleCount++;
            }else {
                negSampleCount++;
            }
        }
        prob.l=l;
        prob.x=X;
        prob.y=y;
        
        System.out.println("加载了"+prob.l+"个样本，其中正样本"+posSampleCount+"，负样本"+negSampleCount);
        
        return prob;
    }
    
    public svm_node[] buildSvmNodes(SampleVO sample) {
        int xLength=sample.getxLength();
        svm_node[] nodeArr=new svm_node[xLength]; 
        for (int index  = 1; index <= xLength; index++) {
            //index从1开始
            svm_node node=new svm_node();
            double value=sample.getWordCount(index);
            node.index=index;
            node.value=value;
            nodeArr[index-1]=node;
        }
        return nodeArr;
    }
    
}
